package com.itheima.redis.util;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.params.SetParams;

import java.util.Arrays;

public class SingleRedisLockDemo {

    //定义锁的超时时间
    private Long timeOut=30000l;

    //定义一个jedis的连接池
    JedisPool jedisPool =new JedisPool("106.53.254.147",6379);

    public Boolean addLock(String lockKey,Long time){

        long id = Thread.currentThread().getId();
        //唯一标识
        String tid=String.valueOf(id);

        //获得jedis连接对象
        Jedis jedis = this.jedisPool.getResource();


        //Ver .01
        //失败就循环来获取锁
//        for (;;){
//            //获取锁的操作直接写在循环中
//            try {
//                //加锁方式优化，这样写貌似无法设置过期时间
//                Long resultId = jedis.setnx(lockKey, tid);
//                String resStr = jedis.set(lockKey, tid, SetParams.setParams().nx().px(30000));
//
//                if (1==resultId){
        //如果程序在这里崩溃还可能发生死锁问题
//                    jedis.expire(lockKey,30);
//                    System.out.println("线程 "+tid+" 加锁成功");
//                    return true;
//                }
//                Thread.sleep(100);
//            }catch (Exception e){
//                System.out.println("出现异常");
//            }finally {
//                jedis.close();
//            }
//
//        }

        // Ver .02 设置过期时间，防止分开设置由于服务崩溃引起的死锁
        //失败就循环来获取锁
//        for (;;){
//            //获取锁的操作直接写在循环中
//            try {
//                //加锁方式优化，这样写貌似无法设置过期时间
//                String resStr = jedis.set(lockKey, tid, SetParams.setParams().nx().px(30000));
//                if ("OK".equals(resStr)){
//                    System.out.println("线程 "+tid+" 加锁成功");
//                    return true;
//                }
//                Thread.sleep(100);
//            }catch (Exception e){
//                System.out.println("出现异常");
//            }finally {
//                jedis.close();
//            }
//        }

        for (;;){
            //加锁前获取当前时间
            long start = System.currentTimeMillis();

            //获取锁的操作直接写在循环中
            try {
                //加锁方式优化，这样写貌似无法设置过期时间
                String resStr = jedis.set(lockKey, tid, SetParams.setParams().nx().px(30000));
                if ("OK".equals(resStr)){
                    System.out.println("线程 "+tid+" 加锁成功");
                    return true;
                }
                Thread.sleep(100);

                long finshTime = System.currentTimeMillis() - start;

                if (finshTime > time){
                    System.out.println(" 获取锁超时 ");
                    return false;
                }


            }catch (Exception e){
                System.out.println("出现异常");
            }finally {
                jedis.close();
            }
        }

    }


    //加锁的方法
    public Boolean addLock(String lockKey){
        return addLock(lockKey,timeOut);
    }

    //Ver .01
    //解锁操作
//    public Boolean replaceLock(String lockKey){
//
//        //判断一下key是否存在，然后判断tid是否相同，然后删除key
//
//        long id = Thread.currentThread().getId();
//        String tid=String.valueOf(id);
//
//        Jedis jedis = this.jedisPool.getResource();
//
//        if (jedis.exists(lockKey)){
//            if (jedis.get(lockKey).equals(tid)){
//                jedis.del(lockKey);
//                System.out.println("线程 "+tid+" 解锁成功");
//                return true;
//            }else {
//                System.out.println("解锁失败");
//                return false;
//            }
//        }else{
//            System.out.println("锁不存在---");
//            return false;
//        }
//
//    }

    //Ver .02 使用lua脚本实现解锁的原子操作
    //解锁操作
    public Boolean replaceLock(String lockKey){

        //判断一下key是否存在，然后判断tid是否相同，然后删除key

        long id = Thread.currentThread().getId();
        String tid=String.valueOf(id);

        Jedis jedis = this.jedisPool.getResource();

        String lua ="if redis.call('get',KEYS[1]) == ARGV[1] then" +
                "    return redis.call('del',KEYS[1])" +
                "else" +
                "     return 0 " +
                "end ";
        Long result = (Long) jedis.eval(lua, Arrays.asList(lockKey), Arrays.asList(tid));
        if (1==result){
            System.out.println("线程 "+tid+" 解锁成功");
            return true;
        }else {
            System.out.println("线程 "+tid+" 解锁失败");
            return false;
        }



    }

}
